package com.thunbu.lib_common.utils.rsa;

import javax.crypto.Cipher;
import java.io.ByteArrayOutputStream;
import java.security.Key;
import java.security.KeyFactory;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;


public class RSA {
    /**
     * 加密算法RSA
     */
    public static final String KEY_ALGORITHM = "RSA";
    /**
     * 签名算法
     */
    public static final String SIGNATURE_ALGORITHM = "SHA1WithRSA";
    //MD5withRSA
    /**
     * RSA最大加密明文大小
     */
    private static final int MAX_ENCRYPT_BLOCK = 117;
    /**
     * RSA最大解密密文大小
     */
    private static final int MAX_DECRYPT_BLOCK = 256;

    /**
     * RSA签名
     * @param content 待签名数据
     * @param privateKey 商户私钥
     * @param input_charset 编码格式
     * @return 签名值
     */
    public static String sign(String content, String privateKey, String input_charset)
    {
        try
        {
            PKCS8EncodedKeySpec priPKCS8 	= new PKCS8EncodedKeySpec(Base64.decode(privateKey) );
            KeyFactory keyf 				= KeyFactory.getInstance("RSA");
            PrivateKey priKey 				= keyf.generatePrivate(priPKCS8);

            java.security.Signature signature = java.security.Signature
                    .getInstance(SIGNATURE_ALGORITHM);

            signature.initSign(priKey);
            signature.update( content.getBytes(input_charset) );

            byte[] signed = signature.sign();

            return Base64.encode(signed);
        }
        catch (Exception e)
        {
            e.printStackTrace();
        }

        return null;
    }

    /**
     * RSA验签名检查
     * @param content 待签名数据
     * @param sign 签名值
     * @param publicKey 支付宝公钥
     * @param input_charset 编码格式
     * @return 布尔值
     */
    public static boolean verify(String content, String sign, String publicKey, String input_charset)
    {
        try
        {
            KeyFactory keyFactory = KeyFactory.getInstance(RSA.KEY_ALGORITHM);
            byte[] encodedKey = Base64.decode(publicKey);
            PublicKey pubKey = keyFactory.generatePublic(new X509EncodedKeySpec(encodedKey));


            java.security.Signature signature = java.security.Signature
                    .getInstance(SIGNATURE_ALGORITHM);

            signature.initVerify(pubKey);
            signature.update( content.getBytes(input_charset) );

            boolean bverify = signature.verify( Base64.decode(sign) );
            return bverify;

        }
        catch (Exception e)
        {
            //签名错误时，Base64.decode(sign)返回null,此处不需要打印异常，如果打印出来容易误导人
            //e.printStackTrace();
        }

        return false;
    }

    /**
     *  通过私钥对原始值进行加密，返回加密串（Base64字符串）
     *  <pre>
     * 	    该方法需要配合decryptByPublicKey进行解密
     * 	</pre>
     * @param data 待签名数据
     * @param privateKey 私钥值
     * @return 加密串
     */
    public static String encryptByPrivateKey(String data, String privateKey) throws Exception{
        byte[] bytes = encryptByPrivateKey(data.getBytes(),privateKey);
        return Base64.encode(bytes);
    }
    /**
     *  通过公钥对原始值进行加密，返回加密串（Base64字符串）
     *  <pre>
     *      该方法需要配合decryptByPrivateKey进行解密
     *  </pre>
     * @param data 待签名数据
     * @param publicKey 公钥值
     * @return 加密串
     */
    public static String encryptByPublicKey(String data , String publicKey) throws Exception{
        byte[] bytes = encryptByPublicKey(data.getBytes(),publicKey);
        return Base64.encode(bytes);
    }
    /**
     *  通过私钥对加密值（Base64字符串）进行解密，返回原始值
     *  <pre>
     *      该方法的参数base64_data应是通过encryptByPublicKey进行解密串
     *  </pre>
     * @param base64_data 加密值（Base64字符串）
     * @param privateKey 私钥值
     * @return 加密串
     */
    public static String decryptByPrivateKey(String base64_data, String privateKey) throws Exception {
        byte[] data = Base64.decode(base64_data);
        return new String(decryptByPrivateKey(data,privateKey));
    }
    /**
     *  通过公钥对加密值（Base64字符串）进行解密，返回原始值
     *  <pre>
     *      该方法的参数base64_data应是通过encryptByPrivateKey进行解密串
     *  </pre>
     * @param base64_data 加密值（Base64字符串）
     * @param publicKey 公钥值
     * @return 加密串
     */
    public static String decryptByPublicKey(String base64_data, String publicKey) throws Exception {
        byte[] data = Base64.decode(base64_data);
        return new String(decryptByPublicKey(data,publicKey));
    }

    /**
     * <P>
     * 私钥解密
     * </p>
     *
     * @param encryptedData 已加密数据
     * @param privateKey 私钥(BASE64编码)
     * @return 解密内容
     * @throws Exception 异常信息
     */
    private static byte[] decryptByPrivateKey(byte[] encryptedData, String privateKey)
            throws Exception {
        byte[] keyBytes = Base64.decode(privateKey);
        PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes);
        KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
        Key privateK = keyFactory.generatePrivate(pkcs8KeySpec);
        Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
        cipher.init(Cipher.DECRYPT_MODE, privateK);
        int inputLen = encryptedData.length;
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        int offSet = 0;
        byte[] cache;
        int i = 0;
        // 对数据分段解密
        while (inputLen - offSet > 0) {
            if (inputLen - offSet > MAX_DECRYPT_BLOCK) {
                cache = cipher.doFinal(encryptedData, offSet, MAX_DECRYPT_BLOCK);
            } else {
                cache = cipher.doFinal(encryptedData, offSet, inputLen - offSet);
            }
            out.write(cache, 0, cache.length);
            i++;
            offSet = i * MAX_DECRYPT_BLOCK;
        }
        byte[] decryptedData = out.toByteArray();
        out.close();
        return decryptedData;
    }

    /**
     * <p>
     * 公钥解密
     * </p>
     *
     * @param encryptedData 已加密数据
     * @param publicKey 公钥(BASE64编码)
     * @return 公钥解密内容
     * @throws Exception 异常信息
     */
    private static byte[] decryptByPublicKey(byte[] encryptedData, String publicKey)
            throws Exception {
        byte[] keyBytes = Base64.decode(publicKey);
        X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(keyBytes);
        KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
        Key publicK = keyFactory.generatePublic(x509KeySpec);
        Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
        cipher.init(Cipher.DECRYPT_MODE, publicK);
        int inputLen = encryptedData.length;
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        int offSet = 0;
        byte[] cache;
        int i = 0;
        // 对数据分段解密
        while (inputLen - offSet > 0) {
            if (inputLen - offSet > MAX_DECRYPT_BLOCK) {
                cache = cipher.doFinal(encryptedData, offSet, MAX_DECRYPT_BLOCK);
            } else {
                cache = cipher.doFinal(encryptedData, offSet, inputLen - offSet);
            }
            out.write(cache, 0, cache.length);
            i++;
            offSet = i * MAX_DECRYPT_BLOCK;
        }
        byte[] decryptedData = out.toByteArray();
        out.close();
        return decryptedData;
    }

    /**
     * <p>
     * 公钥加密
     * </p>
     *
     * @param data 源数据
     * @param publicKey 公钥(BASE64编码)
     * @return 加密结果值
     * @throws Exception 异常信息
     */
    private static byte[] encryptByPublicKey(byte[] data, String publicKey)
            throws Exception {
        byte[] keyBytes = Base64.decode(publicKey);
        X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(keyBytes);
        KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
        Key publicK = keyFactory.generatePublic(x509KeySpec);
        // 对数据加密
        Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
        cipher.init(Cipher.ENCRYPT_MODE, publicK);
        int inputLen = data.length;
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        int offSet = 0;
        byte[] cache;
        int i = 0;
        // 对数据分段加密
        while (inputLen - offSet > 0) {
            if (inputLen - offSet > MAX_ENCRYPT_BLOCK) {
                cache = cipher.doFinal(data, offSet, MAX_ENCRYPT_BLOCK);
            } else {
                cache = cipher.doFinal(data, offSet, inputLen - offSet);
            }
            out.write(cache, 0, cache.length);
            i++;
            offSet = i * MAX_ENCRYPT_BLOCK;
        }
        byte[] encryptedData = out.toByteArray();
        out.close();
        return encryptedData;
    }

    /**
     * <p>
     * 私钥加密
     * </p>
     *
     * @param data 源数据
     * @param privateKey 私钥(BASE64编码)
     * @return 加密结果值
     * @throws Exception 异常信息
     */
    private static byte[] encryptByPrivateKey(byte[] data, String privateKey)
            throws Exception {
        byte[] keyBytes = Base64.decode(privateKey);
        PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes);
        KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
        Key privateK = keyFactory.generatePrivate(pkcs8KeySpec);
        Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
        cipher.init(Cipher.ENCRYPT_MODE, privateK);
        int inputLen = data.length;
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        int offSet = 0;
        byte[] cache;
        int i = 0;
        // 对数据分段加密
        while (inputLen - offSet > 0) {
            if (inputLen - offSet > MAX_ENCRYPT_BLOCK) {
                cache = cipher.doFinal(data, offSet, MAX_ENCRYPT_BLOCK);
            } else {
                cache = cipher.doFinal(data, offSet, inputLen - offSet);
            }
            out.write(cache, 0, cache.length);
            i++;
            offSet = i * MAX_ENCRYPT_BLOCK;
        }
        byte[] encryptedData = out.toByteArray();
        out.close();
        return encryptedData;
    }

    /**
     * 得到私钥
     * @param key 密钥字符串（经过base64编码）
     * @throws Exception
     */
    private static PrivateKey getPrivateKey(String key) throws Exception {

        byte[] keyBytes;

        keyBytes = Base64.decode(key);

        PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(keyBytes);

        KeyFactory keyFactory = KeyFactory.getInstance(RSA.KEY_ALGORITHM);

        PrivateKey privateKey = keyFactory.generatePrivate(keySpec);

        return privateKey;
    }

    /**
     * 通过公钥字符串得到公钥
     *
     * @param key
     *            公钥字符串（经过base64编码）
     * @throws Exception
     */
    private static PublicKey getPublicKey(String key) throws Exception {
        byte[] keyBytes = Base64.decode(key);

        X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(keyBytes);

        KeyFactory keyFactory = KeyFactory.getInstance(RSA.KEY_ALGORITHM);

        PublicKey publicKey = keyFactory.generatePublic(x509KeySpec);

        return publicKey;
    }


    public static void main(String[] args) throws Exception{
//        RSAKeys keysPair = RSASecrteKey.generateKeyPair();
//        String privateKey = keysPair.getPrivateKey();
//        String publicKey = keysPair.getPublicKey();
//        System.out.println("privateKey : "+privateKey);
//        System.out.println("publicKey : "+publicKey);
//
//        String userId = "13300000001";
//        int ttl = 300;
//        Long start = System.currentTimeMillis();
//        String content = userId+"_"+start+"_"+ttl;
//        String sign = RSA.sign(content, privateKey,"utf-8");
//        System.out.println("sign: "+ sign);
//        System.out.println("verify : "+ RSA.verify(content,sign,publicKey,"utf-8"));
//
//
//
//        String source = "这是一串原始内容这是一串原始内容这是";
//        String sign1 = RSA.sign(source, privateKey,"utf-8");
//        System.out.println("sign1: "+ sign1);
//        System.out.println("verify1 : "+ RSA.verify(source,sign1,publicKey,"utf-8"));
//
//        source = "公钥加密，私钥解密============================";
//        byte[] data = source.getBytes();
//        byte[] encodedData = encryptByPublicKey(data, publicKey);
//        byte[] decodedData = decryptByPrivateKey(encodedData, privateKey);
//        String target = new String(decodedData);
//        System.out.println("解密结果 : "+ source.equals(target));
//
//
//        source = "私钥加密，公钥解密++++++++++++++++++++++++++++";
//        byte[] data1 = source.getBytes();
//        byte[] encodedData1 = encryptByPrivateKey(data1, privateKey);
//        byte[] decodedData1 = decryptByPublicKey(encodedData1, publicKey);
//        String target1 = new String(decodedData1);
//        System.out.println("解密结果 : "+ source.equals(target1));

        String result =RSAUtil.encryptByPublicKey("489LJa%%#",
                "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAqvKQDLr5lcXWFIsnnqSQbvguujbxkPZ6GVgsAJSbFAuwR5KSomr4mw/eb+Cqg/ikidNGODkoOAvI5mcStDHg2ijo2qJYpadzydcxM63835ev28SEpUH1aV2AThhhcGBo8ethpvzUFegs6WAlZviOFQlAAMp+55YO7HtSt5qebWzxKz4sVgQdkZr7748SNUdn7UzfWly8MKpzQDOUbnKPh7GfUmts7uYwzpJoAeeBt2RpoQ2xMjczHqevxbomBeR0TRxGktZs4Kuc6KzI81Hi36wDbIwVPO1YoqgXd42eazZoMVIe8yMFv/mjqEJVPXkBYR3nr+FxPvfJyyYAVdvKhwIDAQAB");

        System.out.println(result);

    }
}
